package sunlight.session.user;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;

import org.apache.axis2.AxisFault;

import sunlight.core.Application;
import sunlight.database.DBConnectionLostException;
import sunlight.database.DatasourceHelper;
import sunlight.database.MySqlDatabase;
import sunlight.logger.EventLogger;
import sunlight.ws.axis2.clients.AuthenticationClient;

/**
 * Utilizata pentru a incapsula si accesa date specifice unui utilizator
 * 
 * @author adrian
 * @version	0.3
 */
public class User{
	
	private    String 	role;
	protected  boolean isAuthenticated;
	protected  String  Username;
	
	protected static String[] AVAIL_LOGIN_METHODS = new String[]{
		"database", "webservice"
	};
	
	protected static String loginMethod;
	
	protected HashMap<String, String> WebServiceAuthorization;
	protected HashMap<String, String> DatabaseAuthorization;
	
	public HashMap<String, Object> Data;
	
	private MySqlDatabase 		Database; 
	private DatasourceHelper	dbHelper;
	
	public User(){
		
		this.dbHelper = new DatasourceHelper();
		this.Database = this.dbHelper.getDbObject();
		
		this.setRole(role);
		this.Data = new HashMap<String, Object>();
		
		this.isAuthenticated = false;
		
		this.DatabaseAuthorization 	= new HashMap<String, String>();
		this.WebServiceAuthorization= new HashMap<String, String>(); 
		
		try {
			this.setLoginMethod(User.AVAIL_LOGIN_METHODS[0]);
		} catch (UnsupportedLoginMethodException e) {
			Application.Log.write(e.toString(), EventLogger.ERROR);
		}
	};
	
	public User(String role){
		this.dbHelper = new DatasourceHelper();
		this.Database = this.dbHelper.getDbObject();
		
		this.setRole(role);
		this.Data = new HashMap<String, Object>();
		
		this.isAuthenticated = false;
		
		this.DatabaseAuthorization 	= new HashMap<String, String>();
		this.WebServiceAuthorization= new HashMap<String, String>();
		
		try {
			this.setLoginMethod(User.AVAIL_LOGIN_METHODS[0]);
		} catch (UnsupportedLoginMethodException e) {
			Application.Log.write(e.toString(), EventLogger.ERROR);
		} 
	};
	
	public void setLoginMethod(String method) throws UnsupportedLoginMethodException{
		for(int i=0; i<User.AVAIL_LOGIN_METHODS.length; i++){
			if(method.equals(User.AVAIL_LOGIN_METHODS[i])){
				User.loginMethod = method;
				this.initLoginMethod(method);
				
				return;
			}
		}
		
		throw new UnsupportedLoginMethodException();
	}
	
	public void initLoginMethod(String method) throws UnsupportedLoginMethodException{
		if(method.equals("database")){
			this.DatabaseAuthorization.put("table"			, "users");
			this.DatabaseAuthorization.put("usernameField"	, "username");
			this.DatabaseAuthorization.put("passwordField"	, "password");
			this.DatabaseAuthorization.put("encryption"		, "MD5");
		}
		
		else if(method.equals("webservice")){
			this.WebServiceAuthorization.put("endpoint"			, "http://localhost:8080/axis2/services/AuthorizationService");
			this.WebServiceAuthorization.put("targetNamespace"	, "http://adrian.sunlight.ws.axis2/xsd");
			this.WebServiceAuthorization.put("loginMethod"		, "login");
			this.WebServiceAuthorization.put("logoutMethod"		, "logout");
			this.WebServiceAuthorization.put("usernameParam"	, "username");
			this.WebServiceAuthorization.put("passwordParam"	, "password");
		}
		
		else{
			throw new UnsupportedLoginMethodException();
		}
	}
	
	public void setLoginByDatabase(String table, String usernameField, String passwordField, String encryption){
		
		User.loginMethod = "database";
		
		this.DatabaseAuthorization.put("table"			, table);
		this.DatabaseAuthorization.put("usernameField"	, usernameField);
		this.DatabaseAuthorization.put("passwordField"	, passwordField);
		this.DatabaseAuthorization.put("encryption"		, encryption);
	}
	
	public void setLoginByWebService(String endpoint, String targetNamespace, String loginMethod, String logoutMethod, 
			String verifyAuthMetod, String validAuthResponse, String usernameParam, String passwordParam){
		
		User.loginMethod = "webservice";
		
		this.WebServiceAuthorization.put("endpoint"			, endpoint);
		this.WebServiceAuthorization.put("targetNamespace"	, targetNamespace);
		this.WebServiceAuthorization.put("loginMethod"		, loginMethod);
		this.WebServiceAuthorization.put("logoutMethod"		, logoutMethod);
		this.WebServiceAuthorization.put("verifyAuthMethod"	, verifyAuthMetod);
		this.WebServiceAuthorization.put("validAuthResponse", validAuthResponse);
		this.WebServiceAuthorization.put("usernameParam"	, usernameParam);
		this.WebServiceAuthorization.put("passwordParam"	, passwordParam);
	}
	
	public String[] getAvailableLoginMethods(String method){
		if(User.AVAIL_LOGIN_METHODS != null){
			return User.AVAIL_LOGIN_METHODS;
		}
		
		return null;
	}
	
	public boolean isAvailableLoginMethod(String method){
		for(int i=0; i<User.AVAIL_LOGIN_METHODS.length; i++){
			if(User.AVAIL_LOGIN_METHODS.equals(method)){
				return true;
			}
		}
		
		return false;
	}
	
	private boolean loginByDatabase(String username, String password, String usersTable, String usernameColumn, String passwordColumn, String encription) throws DBConnectionLostException{
		
		this.Username = username;
		
		if((username != null) && (password != null)){
			HashMap<String, String> credentials = new HashMap<String, String>();
			
			credentials.put("username", username);
			
			try {
				
				ResultSet udata = this.Database.select("*", usersTable, credentials, null, null, " AND `password`="+ encription +"('"+ password +"')", false);
				if(udata.first()){
					this.isAuthenticated = true;
					
					String[] colNames = MySqlDatabase.getColumnNames(udata);
					
					for(int i=0; i<colNames.length; i++){
						this.Data.put(colNames[i], udata.getString(colNames[i]));
					}
					
					return true;
				}
			} catch (SQLException sqlex) {
				Application.Log.write(sqlex.toString(), EventLogger.ERROR);
			}
			catch(NullPointerException nex){
				Application.Log.write(nex.toString(), EventLogger.ERROR);
			} 
		}
		
		return false;
	}
	
	public boolean loginByDatabase(String username, String password) throws DBConnectionLostException{
		return this.loginByDatabase(username, password, this.DatabaseAuthorization.get("table"), 
				this.DatabaseAuthorization.get("usernameField"), this.DatabaseAuthorization.get("passwordField"), 
				this.DatabaseAuthorization.get("encryption")
				);
	}
	
	public boolean loginByDatabase(String username, String password, String table) throws DBConnectionLostException{
		return this.loginByDatabase(username, password, table, this.DatabaseAuthorization.get("usernameField"), this.DatabaseAuthorization.get("passwordField"), 
				this.DatabaseAuthorization.get("encryption")
				);
	}
	
	private boolean loginByWebService(String username, String password, String endpoint, String targetNamespace, String loginMethod, 
			String verifyAuthMetod, String validAuthResponse, String logoutMethod, String usernameParamName, String passwordParamName){
		
		this.Username = username;
		
		try {
			AuthenticationClient ac = new AuthenticationClient(this.WebServiceAuthorization.get("endpoint"), this.WebServiceAuthorization.get("targetNamespace"));
			
			ac.setLoginParams(this.WebServiceAuthorization.get("loginMethod"), this.WebServiceAuthorization.get("usernameParam"), 
					this.WebServiceAuthorization.get("passwordParam"), username, password);
			
			ac.setVerifyAuthenticationParams(this.WebServiceAuthorization.get("verifyAuthMethod"), this.WebServiceAuthorization.get("usernameParam"), username, 
					this.WebServiceAuthorization.get("validAuthResponse"));
			
			ac.login();
			if(ac.isAuthenticated()){
				this.isAuthenticated = true;
				return true;
			}
			
			ac.closeClient();
			
		} catch (AxisFault e) {
			Application.Log.write(e.toString(), EventLogger.ERROR);
		}
		
		return false;
	}
	
	public boolean loginByWebService(String username, String password, String endpoint){
		
		return this.loginByWebService(username, password, endpoint, this.WebServiceAuthorization.get("targetNamespace"),
				this.WebServiceAuthorization.get("loginMethod"), this.WebServiceAuthorization.get("verifyAuthMethod"),
				this.WebServiceAuthorization.get("validAuthResponse"), this.WebServiceAuthorization.get("logoutMethod"), 
				this.WebServiceAuthorization.get("usernameParam"), this.WebServiceAuthorization.get("passwordParam")); 
	}
	
	public boolean loginByWebService(String username, String password){
		
		return this.loginByWebService(username, password, this.WebServiceAuthorization.get("endpoint"),
				this.WebServiceAuthorization.get("targetNamespace"), this.WebServiceAuthorization.get("loginMethod"),
				this.WebServiceAuthorization.get("verifyAuthMethod"), this.WebServiceAuthorization.get("validAuthResponse"),
				this.WebServiceAuthorization.get("logoutMethod"), this.WebServiceAuthorization.get("usernameParam"), this.WebServiceAuthorization.get("passwordParam")); 
	}
	
	/**
	 * initializeaza un obiect de tip User si realizeaza autentificarea acestuia pe baza numelui de utilizator si a parolei
	 * 
	 * @param username
	 * @param password
	 * @return User
	 * @throws DBConnectionLostException
	 * @throws UnsupportedLoginMethodException 
	 */
	public boolean login(String username, String password) throws DBConnectionLostException, UnsupportedLoginMethodException{
		
		if((username!=null) && (password != null)){
			
			if(User.loginMethod.equals("database")){
				return this.loginByDatabase(username, password);
			}
			
			else if(User.loginMethod.equals("webservice")){
				return this.loginByWebService(username, password);
			}
			
			else{
				throw new UnsupportedLoginMethodException();
			}
		}
		
		return false;
	}

	public void logout(){
		if(User.loginMethod.equals("database")){
			this.isAuthenticated = false;
		}
		
		else if(User.loginMethod.equals("webservice")){
			try {
				AuthenticationClient ac = new AuthenticationClient(this.WebServiceAuthorization.get("endpoint"), this.WebServiceAuthorization.get("targetNamespace"));
			
				ac.setLogoutParams(this.WebServiceAuthorization.get("logoutMethod"), this.WebServiceAuthorization.get("usernameParam"), this.Username);
				
				ac.logout();
				
				ac.closeClient();
				
			} catch (AxisFault e) {
				Application.Log.write(e.toString(), EventLogger.ERROR);
			}
		}
		
		else{
			this.isAuthenticated = false;
		}
	}
	
	public boolean hasDatabase(){
		return this.Database.isConnected();
	}
	
	public String get(String dataKey){
		return this.Data.get(dataKey).toString();
	}
	
	public void setRole(String role) {
		this.role = role;
	}
	
	public String getRole() {
		return role;
	}
	
	public String getUsername(){
		return this.Username;
	}
	
	public boolean isAuthenticated(){
		return this.isAuthenticated;
	}
	
}
